#include "rgsvertextool.h"
#include<qapplication.h>
#include<qgsmapmouseevent.h>


#include <qgsguiutils.h>
#include<qgssnappingutils.h>
#include<qgsmapcanvas.h>
#include<qgsmultipoint.h>


class SelectedMatchFilter : public QgsPointLocator::MatchFilter
{
public:
    explicit SelectedMatchFilter( double tol, QgsLockedFeature *selectedFeature )
        : mTolerance( tol )
        , mLockedFeature( selectedFeature ) {}

    bool acceptMatch( const QgsPointLocator::Match &match ) override
    {
        if ( match.distance() <= mTolerance && match.layer() )
        {
            // option 1: we have "locked" feature - we consider just a match from that feature
            // option 2: we do not have "locked" feature - we consider matches from any selected feature
            if ( ( mLockedFeature && mLockedFeature->layer() == match.layer() && mLockedFeature->featureId() == match.featureId() )
                || ( !mLockedFeature && match.layer()->selectedFeatureIds().contains( match.featureId() ) ) )
            {
                if ( !mBestSelectedMatch.isValid() || match.distance() < mBestSelectedMatch.distance() )
                    mBestSelectedMatch = match;
            }
        }
        return true;
    }

    bool hasSelectedMatch() const { return mBestSelectedMatch.isValid(); }
    QgsPointLocator::Match bestSelectedMatch() const { return mBestSelectedMatch; }

private:
    double mTolerance;
    QgsLockedFeature *mLockedFeature;   // not null in case of selected (locked) feature
    QgsPointLocator::Match mBestSelectedMatch;
};


class OneFeatureFilter : public QgsPointLocator::MatchFilter
{
public:
    OneFeatureFilter( const QgsVectorLayer *layer, QgsFeatureId fid )
        : layer( layer )
        , fid( fid )
    {}

    bool acceptMatch( const QgsPointLocator::Match &match ) override
    {
        return match.layer() == layer && match.featureId() == fid;
    }

private:
    const QgsVectorLayer *layer = nullptr;
    QgsFeatureId fid;
};



RgsVertexTool::RgsVertexTool(QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDock, VertexToolMode mode)
    :QgsMapToolAdvancedDigitizing(canvas,cadDock),mMode(mode)
{
    setAdvancedDigitizingAllowed( false );

    mFeatureBand = createRubberBand( QgsWkbTypes::LineGeometry );
    mFeatureBand->setVisible( false );

    QColor color = digitizingStrokeColor();
    mFeatureBandMarkers = new QgsRubberBand( canvas );
    mFeatureBandMarkers->setIcon( QgsRubberBand::ICON_CIRCLE );
    mFeatureBandMarkers->setColor( color );
    mFeatureBandMarkers->setWidth( QgsGuiUtils::scaleIconSize( 2 ) );
    mFeatureBandMarkers->setBrushStyle( Qt::NoBrush );
    mFeatureBandMarkers->setIconSize( QgsGuiUtils::scaleIconSize( 6 ) );
    mFeatureBandMarkers->setVisible( false );

    mVertexBand = new QgsRubberBand( canvas );
    mVertexBand->setIcon( QgsRubberBand::ICON_CIRCLE );
    mVertexBand->setColor( color );
    mVertexBand->setWidth( QgsGuiUtils::scaleIconSize( 2 ) );
    mVertexBand->setBrushStyle( Qt::NoBrush );
    mVertexBand->setIconSize( QgsGuiUtils::scaleIconSize( 15 ) );
    mVertexBand->setVisible( false );


}

RgsVertexTool::~RgsVertexTool()
{
    delete mFeatureBand;
    delete mFeatureBandMarkers;
    delete mVertexBand;
}

void RgsVertexTool::cadCanvasReleaseEvent(QgsMapMouseEvent *e)
{
    if(mNewVertexFromDoubleClick){

    }
    else{
        if(e->button()==Qt::LeftButton){
            if(mDraggingVertex){

            }
            else{
                startDragging( e );
            }
        }
    }
}

void RgsVertexTool::cadCanvasMoveEvent(QgsMapMouseEvent *e)
{
    //qDebug()<<u8"vertex移动事件";
    if ( mLockedFeatureAlternatives && ( e->pos() - mLockedFeatureAlternatives->screenPoint ).manhattanLength() >= QApplication::startDragDistance() )
    {
        // as soon as the mouse moves more than just a tiny bit, previously stored alternatives info
        // is probably not valid anymore and will need to be re-calculated
        mLockedFeatureAlternatives.reset();
    }

    //暂时不提供其他选择模型，只能是正常点选顶点
    if ( mSelectionMethod == SelectionRange )
    {
        rangeMethodMoveEvent( e );
        return;
    }

    if(mDraggingVertex){
        mouseMoveDraggingVertex( e );
    }
    else{
        mouseMoveNotDragging(e);
    }

}

void RgsVertexTool::activate()
{

    QgsMapToolAdvancedDigitizing::activate();
}

void RgsVertexTool::rangeMethodMoveEvent(QgsMapMouseEvent *e)
{

}

void RgsVertexTool::mouseMoveNotDragging(QgsMapMouseEvent *e)
{
    QgsPointLocator::Match m = snapToEditableLayer( e );
    bool targetIsAllowed = ( !mLockedFeature || ( mLockedFeature->featureId() == m.featureId() && mLockedFeature->layer() == m.layer() ) );

    if ( m.type() == QgsPointLocator::Vertex && targetIsAllowed ){
        updateVertexBand( m );

    }
    else{
        mVertexBand->setVisible(false);
    }

    if ( !m.isValid() )
    {
        // as the last resort check if we are on top of a feature if there is no vertex or edge snap
        m = snapToPolygonInterior( e );
    }

    updateFeatureBand( mLockedFeature ? QgsPointLocator::Match() : m );
}

QgsPointLocator::Match RgsVertexTool::snapToEditableLayer(QgsMapMouseEvent *e)
{
    QgsSnappingUtils *snapUtils = canvas()->snappingUtils();
    QgsSnappingConfig oldConfig = snapUtils->config();
    QgsPointLocator::Match m;

    QgsPointXY mapPoint = toMapCoordinates( e->pos() );
    double tol = QgsTolerance::vertexSearchRadius( canvas()->mapSettings() );

    QgsSnappingConfig config = oldConfig;
    config.setEnabled( true );
    config.setMode( Qgis::SnappingMode::AdvancedConfiguration );
    config.setIntersectionSnapping( false );  // only snap to layers
    config.clearIndividualLayerSettings();

    typedef QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings> SettingsHashMap;
    SettingsHashMap oldLayerSettings = oldConfig.individualLayerSettings();

    // if there is a current layer, it should have priority over other layers
    // because sometimes there may be match from multiple layers at one location
    // and selecting current layer is an easy way for the user to prioritize a layer
    if ( QgsVectorLayer *currentVlayer = currentVectorLayer() )
    {
        if ( currentVlayer->isEditable() )
        {
            const auto layers = canvas()->layers( true );
            for ( QgsMapLayer *layer : layers )
            {
                QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
                if ( !vlayer )
                    continue;

                QgsSnappingConfig::IndividualLayerSettings layerSettings;
                SettingsHashMap::const_iterator existingSettings = oldLayerSettings.find( vlayer );
                if ( existingSettings != oldLayerSettings.constEnd() )
                {
                    layerSettings = existingSettings.value();
                    layerSettings.setEnabled( vlayer == currentVlayer );
                    layerSettings.setTolerance( tol );
                    layerSettings.setTypeFlag( static_cast<Qgis::SnappingTypes>( Qgis::SnappingType::Vertex | Qgis::SnappingType::Segment ) );
                    layerSettings.setUnits( QgsTolerance::ProjectUnits );
                }
                else
                {
                    layerSettings = QgsSnappingConfig::IndividualLayerSettings(
                        vlayer == currentVlayer, static_cast<Qgis::SnappingTypes>( Qgis::SnappingType::Vertex | Qgis::SnappingType::Segment ), tol, QgsTolerance::ProjectUnits, 0.0, 0.0 );
                }

                config.setIndividualLayerSettings( vlayer, layerSettings );
            }

            snapUtils->setConfig( config );
            SelectedMatchFilter filter( tol, mLockedFeature.get() );
            m = snapUtils->snapToMap( mapPoint, &filter, true );

            // we give priority to snap matches that are from selected features
            if ( filter.hasSelectedMatch() )
            {
                m = filter.bestSelectedMatch();
                mLastSnap.reset();
            }
        }
    }

    // if there is no match from the current layer, try to use any editable vector layer
    if ( !m.isValid() && mMode == AllLayers )
    {
        const auto layers = canvas()->layers( true );
        for ( QgsMapLayer *layer : layers )
        {
            QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
            if ( !vlayer )
                continue;

            QgsSnappingConfig::IndividualLayerSettings layerSettings;
            SettingsHashMap::const_iterator existingSettings = oldLayerSettings.find( vlayer );
            if ( existingSettings != oldLayerSettings.constEnd() )
            {
                layerSettings = existingSettings.value();
                layerSettings.setEnabled( vlayer->isEditable() );
                layerSettings.setTolerance( tol );
                layerSettings.setTypeFlag( static_cast<Qgis::SnappingTypes>( Qgis::SnappingType::Vertex | Qgis::SnappingType::Segment ) );
                layerSettings.setUnits( QgsTolerance::ProjectUnits );
            }
            else
            {
                layerSettings = QgsSnappingConfig::IndividualLayerSettings( vlayer->isEditable(), static_cast<Qgis::SnappingTypes>( Qgis::SnappingType::Vertex | Qgis::SnappingType::Segment ), tol, QgsTolerance::ProjectUnits, 0.0, 0.0 );
            }
            config.setIndividualLayerSettings( vlayer, layerSettings );
        }

        snapUtils->setConfig( config );
        SelectedMatchFilter filter( tol, mLockedFeature.get() );
        m = snapUtils->snapToMap( mapPoint, &filter, true );

        // we give priority to snap matches that are from selected features
        if ( filter.hasSelectedMatch() )
        {
            m = filter.bestSelectedMatch();
            mLastSnap.reset();
        }
    }

    // try to stay snapped to previously used feature
    // so the highlight does not jump around at vertices where features are joined
    if ( mLastSnap )
    {
        OneFeatureFilter filterLast( mLastSnap->layer(), mLastSnap->featureId() );
        QgsPointLocator::Match lastMatch = snapUtils->snapToMap( mapPoint, &filterLast, true );
        // but skip the the previously used feature if it would only snap to segment, while now we have snap to vertex
        // so that if there is a point on a line, it gets priority (as is usual with combined vertex+segment snapping)
        bool matchHasVertexLastHasEdge = m.hasVertex() && lastMatch.hasEdge();
        if ( lastMatch.isValid() && lastMatch.distance() <= m.distance() && !matchHasVertexLastHasEdge )
        {
            m = lastMatch;
        }
    }

    snapUtils->setConfig( oldConfig );

    mLastSnap.reset( new QgsPointLocator::Match( m ) );

    return m;
}

void RgsVertexTool::updateVertexBand(const QgsPointLocator::Match &m)
{
    if ( m.hasVertex() && m.layer() )
    {
        mVertexBand->setToGeometry( QgsGeometry::fromPointXY( m.point() ), nullptr );
        mVertexBand->setVisible( true );
        bool isCircular = false;
        if ( m.layer() )
        {
            isCircular = isCircularVertex( cachedGeometry( m.layer(), m.featureId() ), m.vertexIndex() );
        }

        mVertexBand->setIcon( isCircular ? QgsRubberBand::ICON_FULL_DIAMOND : QgsRubberBand::ICON_CIRCLE );
    }
    else
    {
        mVertexBand->setVisible( false );
    }
}

QgsPointLocator::Match RgsVertexTool::snapToPolygonInterior(QgsMapMouseEvent *e)
{
    QgsSnappingUtils *snapUtils = canvas()->snappingUtils();
    QgsPointLocator::Match m;

    QgsPointXY mapPoint = toMapCoordinates( e->pos() );

    // if there is a current layer, it should have priority over other layers
    // because sometimes there may be match from multiple layers at one location
    // and selecting current layer is an easy way for the user to prioritize a layer
    if ( QgsVectorLayer *currentVlayer = currentVectorLayer() )
    {
        if ( currentVlayer->isEditable() && currentVlayer->geometryType() == QgsWkbTypes::PolygonGeometry )
        {
            QgsPointLocator::MatchList matchList = snapUtils->locatorForLayer( currentVlayer )->pointInPolygon( mapPoint, true );
            if ( !matchList.isEmpty() )
            {
                m = matchList.first();
            }
        }
    }

    // if there is no match from the current layer, try to use any editable vector layer
    if ( !m.isValid() && mMode == AllLayers )
    {
        const auto layers = canvas()->layers( true );
        for ( QgsMapLayer *layer : layers )
        {
            QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
            if ( !vlayer )
                continue;

            if ( vlayer->isEditable() && vlayer->geometryType() == QgsWkbTypes::PolygonGeometry )
            {
                QgsPointLocator::MatchList matchList = snapUtils->locatorForLayer( vlayer )->pointInPolygon( mapPoint, true );
                if ( !matchList.isEmpty() )
                {
                    m = matchList.first();
                    break;
                }
            }
        }
    }

    // if we don't have anything in the last snap, keep the area match
    if ( !mLastSnap && m.isValid() )
    {
        mLastSnap.reset( new QgsPointLocator::Match( m ) );
    }

    return m;
}

void RgsVertexTool::updateFeatureBand(const QgsPointLocator::Match &m)
{
    // highlight feature
    if ( m.isValid() && m.layer() )
    {
        if ( mFeatureBandLayer == m.layer() && mFeatureBandFid == m.featureId() )
            return;  // skip regeneration of rubber band if not needed

        QgsGeometry geom = cachedGeometry( m.layer(), m.featureId() );
        mFeatureBandMarkers->setToGeometry( geometryToMultiPoint( geom ), m.layer() );
        mFeatureBandMarkers->setVisible( true );
        if ( QgsWkbTypes::isCurvedType( geom.wkbType() ) )
            geom = QgsGeometry( geom.constGet()->segmentize() );
        mFeatureBand->setToGeometry( geom, m.layer() );
        mFeatureBand->setVisible( true );
        mFeatureBandLayer = m.layer();
        mFeatureBandFid = m.featureId();
    }
    else
    {
        mFeatureBand->setVisible( false );
        mFeatureBandMarkers->setVisible( false );
        mFeatureBandLayer = nullptr;
        mFeatureBandFid = QgsFeatureId();
    }
}

QgsGeometry RgsVertexTool::cachedGeometry(const QgsVectorLayer *layer, QgsFeatureId fid)
{
    const bool layerWasNotInCache = !mCache.contains( layer );
    // insert if it was not in cache
    QHash<QgsFeatureId, QgsGeometry> &layerCache = mCache[layer];
    if ( layerWasNotInCache )
    {
        //connect( layer, &QgsVectorLayer::geometryChanged, this, &QgsVertexTool::onCachedGeometryChanged );
        //connect( layer, &QgsVectorLayer::featureDeleted, this, &QgsVertexTool::onCachedGeometryDeleted );
        //connect( layer, &QgsVectorLayer::willBeDeleted, this, &QgsVertexTool::clearGeometryCache );
        //connect( layer, &QgsVectorLayer::dataChanged, this, &QgsVertexTool::clearGeometryCache );
    }

    if ( !layerCache.contains( fid ) )
    {
        QgsFeature f;
        layer->getFeatures( QgsFeatureRequest( fid ).setNoAttributes() ).nextFeature( f );
        layerCache[fid] = f.geometry();
    }

    return layerCache[fid];
}

QgsGeometry RgsVertexTool::geometryToMultiPoint(const QgsGeometry &geom)
{
    QgsMultiPoint *multiPoint = new QgsMultiPoint();
    QgsGeometry outputGeom( multiPoint );
    for ( auto pointIt = geom.vertices_begin(); pointIt != geom.vertices_end(); ++pointIt )
        multiPoint->addGeometry( ( *pointIt ).clone() );
    return outputGeom;
}

bool RgsVertexTool::isCircularVertex(const QgsGeometry &geom, int vertexIndex)
{
    QgsVertexId vid;
    return geom.vertexIdFromVertexNr( vertexIndex, vid ) && vid.type == Qgis::VertexType::Curve;
}

void RgsVertexTool::startDragging(QgsMapMouseEvent *e)
{
    QgsPointXY mapPoint = toMapCoordinates( e->pos() );
    QgsPointLocator::Match m = snapToEditableLayer( e );

    if ( !m.isValid() )
        return;

    if ( mLockedFeature && ( mLockedFeature->featureId() != m.featureId() || mLockedFeature->layer() != m.layer() ) )
        return; // when a feature is bound to the vertex editor, only process actions for that feature

    //setAdvancedDigitizingAllowed( true );
    if(m.hasEdge()){

    }
    else{//vertex
        startDraggingMoveVertex( m );
    }
}

void RgsVertexTool::startDraggingMoveVertex(const QgsPointLocator::Match &m)
{
    QgsGeometry geom = cachedGeometry( m.layer(), m.featureId() );

    // start dragging of snapped point of current layer
    mDraggingVertex.reset( new Vertex( m.layer(), m.featureId(), m.vertexIndex() ) );
    mDraggingVertexType = MovingVertex;

    setHighlightedVerticesVisible( false );


    QSet<Vertex> movingVertices;
    movingVertices << *mDraggingVertex;

    QgsPointXY dragVertexMapPoint = m.point();

    buildDragBandsForVertices( movingVertices, dragVertexMapPoint );

}

void RgsVertexTool::setHighlightedVerticesVisible(bool visiable)
{
    mFeatureBand->setVisible(visiable);
    mFeatureBandMarkers->setVisible(visiable);
    mVertexBand->setVisible(visiable);
}

void RgsVertexTool::buildDragBandsForVertices(const QSet<Vertex> &movingVertices, const QgsPointXY &dragVertexMapPoint)
{
    QSet<Vertex> verticesInStraightBands;  // always the vertex with lower index

    // set of middle vertices that are already in a circular rubber band
    // i.e. every circular band is defined by its middle circular vertex
    QSet<Vertex> verticesInCircularBands;

    for ( const Vertex &v : std::as_const( movingVertices ) )
    {
        int v0idx, v1idx;
        QgsGeometry geom = cachedGeometry( v.layer, v.fid );
        QgsPointXY pt = geom.vertexAt( v.vertexId );

        geom.adjacentVertices( v.vertexId, v0idx, v1idx );

        if ( v0idx != -1 && v1idx != -1 && isCircularVertex( geom, v.vertexId ) )
        {
            // the vertex is in the middle of a curved segment
            if ( !verticesInCircularBands.contains( v ) )
            {
                addDragCircularBand( v.layer,
                                    geom.vertexAt( v0idx ),
                                    pt,
                                    geom.vertexAt( v1idx ),
                                    movingVertices.contains( Vertex( v.layer, v.fid, v0idx ) ),
                                    true,
                                    movingVertices.contains( Vertex( v.layer, v.fid, v1idx ) ),
                                    dragVertexMapPoint );
                verticesInCircularBands << v;
            }

            // skip the rest - no need for further straight of circular bands for this vertex
            // because our circular rubber band spans both towards left and right
            continue;
        }

        if ( v0idx != -1 )
        {
            // there is another vertex to the left - let's build a rubber band for it
            Vertex v0( v.layer, v.fid, v0idx );
            if ( isCircularVertex( geom, v0idx ) )
            {
                // circular segment to the left
                if ( !verticesInCircularBands.contains( v0 ) )
                {
                    addDragCircularBand( v.layer,
                                        geom.vertexAt( v0idx - 1 ),
                                        geom.vertexAt( v0idx ),
                                        pt,
                                        movingVertices.contains( Vertex( v.layer, v.fid, v0idx - 1 ) ),
                                        movingVertices.contains( Vertex( v.layer, v.fid, v0idx ) ),
                                        true,
                                        dragVertexMapPoint );
                    verticesInCircularBands << v0;
                }
            }
            else
            {
                // straight segment to the left
                if ( !verticesInStraightBands.contains( v0 ) )
                {
                    addDragStraightBand( v.layer,
                                        geom.vertexAt( v0idx ),
                                        pt,
                                        movingVertices.contains( v0 ),
                                        true,
                                        dragVertexMapPoint );
                    verticesInStraightBands << v0;
                }
            }
        }

        if ( v1idx != -1 )
        {
            // there is another vertex to the right - let's build a rubber band for it
            Vertex v1( v.layer, v.fid, v1idx );
            if ( isCircularVertex( geom, v1idx ) )
            {
                // circular segment to the right
                if ( !verticesInCircularBands.contains( v1 ) )
                {
                    addDragCircularBand( v.layer,
                                        pt,
                                        geom.vertexAt( v1idx ),
                                        geom.vertexAt( v1idx + 1 ),
                                        true,
                                        movingVertices.contains( v1 ),
                                        movingVertices.contains( Vertex( v.layer, v.fid, v1idx + 1 ) ),
                                        dragVertexMapPoint );
                    verticesInCircularBands << v1;
                }
            }
            else
            {
                // straight segment to the right
                if ( !verticesInStraightBands.contains( v ) )
                {
                    addDragStraightBand( v.layer,
                                        pt,
                                        geom.vertexAt( v1idx ),
                                        true,
                                        movingVertices.contains( v1 ),
                                        dragVertexMapPoint );
                    verticesInStraightBands << v;
                }
            }
        }

        if ( v0idx == -1 && v1idx == -1 )
        {
            // this is a standalone point - we need to use a marker for it
            // to give some feedback to the user

            QgsPointXY ptMapPoint = toMapCoordinates( v.layer, pt );
            QgsVertexMarker *marker = new QgsVertexMarker( mCanvas );
            marker->setIconType( QgsVertexMarker::ICON_X );
            marker->setColor( Qt::red );
            marker->setIconSize( QgsGuiUtils::scaleIconSize( 10 ) );
            marker->setPenWidth( QgsGuiUtils::scaleIconSize( 3 ) );
            marker->setVisible( true );
            marker->setCenter( ptMapPoint );
            mDragPointMarkers << marker;
            mDragPointMarkersOffset << ( ptMapPoint - dragVertexMapPoint );
        }
    }
}

void RgsVertexTool::addDragCircularBand(QgsVectorLayer *layer, QgsPointXY v0, QgsPointXY v1, QgsPointXY v2, bool moving0, bool moving1, bool moving2, const QgsPointXY &mapPoint)
{
    if ( layer )
    {
        v0 = toMapCoordinates( layer, v0 );
        v1 = toMapCoordinates( layer, v1 );
        v2 = toMapCoordinates( layer, v2 );
    }

    CircularBand b;
    b.band = createRubberBand( QgsWkbTypes::LineGeometry, true );
    b.p0 = v0;
    b.p1 = v1;
    b.p2 = v2;
    b.moving0 = moving0;
    b.moving1 = moving1;
    b.moving2 = moving2;
    b.offset0 = v0 - mapPoint;
    b.offset1 = v1 - mapPoint;
    b.offset2 = v2 - mapPoint;
    b.updateRubberBand( mapPoint );

    mDragCircularBands << b;
}

void RgsVertexTool::addDragStraightBand(QgsVectorLayer *layer, QgsPointXY v0, QgsPointXY v1, bool moving0, bool moving1, const QgsPointXY &mapPoint)
{
    if ( layer )
    {
        v0 = toMapCoordinates( layer, v0 );
        v1 = toMapCoordinates( layer, v1 );
    }

    StraightBand b;
    b.band = createRubberBand( QgsWkbTypes::LineGeometry, true );
    b.p0 = v0;
    b.p1 = v1;
    b.moving0 = moving0;
    b.moving1 = moving1;
    b.offset0 = v0 - mapPoint;
    b.offset1 = v1 - mapPoint;

    b.band->addPoint( v0 );
    b.band->addPoint( v1 );

    mDragStraightBands << b;
}

void RgsVertexTool::mouseMoveDraggingVertex(QgsMapMouseEvent *e)
{

}

void RgsVertexTool::CircularBand::updateRubberBand(const QgsPointXY &mapPoint)
{
    QgsPointSequence points;
    QgsPointXY v0 = moving0 ? mapPoint + offset0 : p0;
    QgsPointXY v1 = moving1 ? mapPoint + offset1 : p1;
    QgsPointXY v2 = moving2 ? mapPoint + offset2 : p2;
    QgsGeometryUtils::segmentizeArc( QgsPoint( v0 ), QgsPoint( v1 ), QgsPoint( v2 ), points );
    // it would be useful to have QgsRubberBand::setPoints() call
    band->reset();
    for ( const QgsPoint &p : std::as_const( points ) )
        band->addPoint( p );
}
